/*
 * Try to send data end-to-end through a LrWpanMac <-> LrWpanPhy <->
 * SpectrumChannel <-> LrWpanPhy <-> LrWpanMac chain
 *
 * Trace Phy state changes, and Mac DataIndication and DataConfirm events
 * to stdout
 */
#include <ns3/log.h>
#include <ns3/core-module.h>
#include <ns3/lr-wpan-module.h>
#include <ns3/propagation-loss-model.h>
#include <ns3/propagation-delay-model.h>
#include <ns3/simulator.h>
#include <ns3/single-model-spectrum-channel.h>
#include <ns3/constant-position-mobility-model.h>
#include <ns3/packet.h>

#include <iostream>

using namespace ns3;

static void DataIndication (McpsDataIndicationParams params, Ptr<Packet> p)
{
  NS_LOG_UNCOND ("Received packet of size " << p->GetSize ()  << "" );
}

static void DataConfirm (McpsDataConfirmParams params)
{
  NS_LOG_UNCOND ("LrWpanMcpsDataConfirmStatus = " << params.m_status);
}
#if 0
static void StateChangeNotification (std::string context, Time now, LrWpanPhyEnumeration oldState, LrWpanPhyEnumeration newState)
{
  NS_LOG_UNCOND (context << " state change at " << now.GetSeconds ()
                         << " from " << LrWpanHelper::LrWpanPhyEnumerationPrinter (oldState)
                         << " to " << LrWpanHelper::LrWpanPhyEnumerationPrinter (newState));
}


void SendOnePacket (Ptr<LrWpanPhy> sender, Ptr<LrWpanPhy> receiver)
{
    uint32_t n = 10;
    Ptr<Packet> p = Create<Packet> (n);
    sender->PdDataRequest (p->GetSize (), p);
}
#endif

int main (int argc, char *argv[])
{
  bool verbose = false;

  CommandLine cmd;

  cmd.AddValue ("verbose", "turn on all log components", verbose);

  cmd.Parse (argc, argv);

  LrWpanHelper lrWpanHelper;
  if (verbose)
    {
      lrWpanHelper.EnableLogComponents ();
    }

  // Enable calculation of FCS in the trailers. Only necessary when interacting with real devices or wireshark.
  // GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true));

  // Create 2 nodesi, and a NetDevice for each one
        
  int nNodeCount = 100;

  Ptr<Node> nodes[nNodeCount];
   // Each device must be attached to the same channel
  Ptr<SingleModelSpectrumChannel> channel = CreateObject<SingleModelSpectrumChannel> ();
  Ptr<LogDistancePropagationLossModel> propModel = CreateObject<LogDistancePropagationLossModel> ();
  Ptr<ConstantSpeedPropagationDelayModel> delayModel = CreateObject<ConstantSpeedPropagationDelayModel> ();
  channel->AddPropagationLossModel (propModel);
  channel->SetPropagationDelayModel (delayModel);

  Ptr<ConstantPositionMobilityModel> mobility [nNodeCount];
  Ptr<LrWpanNetDevice> dev[nNodeCount]; 
  McpsDataConfirmCallback cb;
  cb = MakeCallback (&DataConfirm);
  McpsDataIndicationCallback cb1;
  cb1 = MakeCallback (&DataIndication);
 
  for (int i =0;i<nNodeCount;++i)
  {
      nodes[i] = CreateObject <Node> ();
      dev[i]= CreateObject<LrWpanNetDevice> ();
      char acMac[8]="";
      sprintf(acMac,"00:%2hx",i);
      dev[i]->SetAddress (Mac16Address (acMac));
      dev[i]->SetChannel (channel);
      mobility[i]= CreateObject<ConstantPositionMobilityModel> ();
      mobility[i]->SetPosition (Vector (0,i,i));
      dev[i]->GetPhy ()->SetMobility (mobility[i]);
      // To complete configuration, a LrWpanNetDevice must be added to a node
      nodes[i]->AddDevice (dev[i]);      
      dev[i]->GetMac ()->SetMcpsDataConfirmCallback (cb);
      dev[i]->GetMac ()->SetMcpsDataIndicationCallback (cb1);

  }

  // Tracing
#if 0
  lrWpanHelper.EnablePcapAll (std::string ("MLCMS"), true);
  AsciiTraceHelper ascii;
  Ptr<OutputStreamWrapper> stream = ascii.CreateFileStream ("MLCMS.tr");
  lrWpanHelper.EnableAsciiAll (stream);

#endif
   
    McpsDataRequestParams params;
    params.m_srcAddrMode = SHORT_ADDR;
    params.m_dstAddrMode = SHORT_ADDR;
    params.m_dstPanId = 0;
    params.m_txOptions = TX_OPTION_ACK;
    params.m_msduHandle = 0;
    const uint8_t *data= (const uint8_t *)"hello123456789";
    Ptr<Packet> p0 = Create<Packet> (data ,50);  // 50 bytes of dummy data
    params.m_dstAddr = Mac16Address::ConvertFrom ( dev[1]->GetAddress()) ;
    Simulator::ScheduleWithContext (1, Seconds (0.0),
                      &LrWpanMac::McpsDataRequest,
                      dev[0]->GetMac (), params, p0);
    Ptr<Packet> p2 = Create<Packet> (50);  // 60 bytes of dummy data
    params.m_dstAddr =  Mac16Address ( "FF:FF"); 

    //params.m_dstAddr =  Mac16Address::ConvertFrom ( dev[0]->GetAddress()) ;
    Simulator::ScheduleWithContext (2, Seconds (2.0),
                      &LrWpanMac::McpsDataRequest,
                      dev[1]->GetMac (), params, p2);

  Simulator::Run ();

  Simulator::Destroy ();
  return 0;
}
